<?php

class CartWS_Model_FormulaEngine_Engine {

    const RET_STOP = 1;
    const RET_TYPE_STOP = 2;
    const RET_CONTINUE = 3;

    protected static $_instance = null;

    protected $_changedOrderIds = array();

    protected $_db;

    protected function __construct() {
    }

    private function __clone() {
    }

    /**
     * Singleton instance
     */
    public static function getInstance() {
        if (null === self::$_instance) {
            self::$_instance = new self();
        }
        return self::$_instance;
    }

    public function getDb() {
        return $this->db;
    }

    public function setDb($db) {
        $this->_db = $db;
    }

    /**
     * Called by CartWS_Model_FormulaEngine_Table to set the orders that has changed.
     *
     * @param string $tableName
     * @param integer $orderId
     */
    public function setChanged( $tableName, $orderId ) {
    // Se podria agregar una tabla para que las formulas se registren para
    // dispararse ante ciertos cambios especificos (linea de la orden, propiedades, etc, etc)
    // Por ahora se ignora el paramentro tableName (se podria evaluar mirar la columna tambien) y se disparan todas las formulas
    // siempre
        if ( ! isset( $this->_changedOrderIds[ $orderId ] ) ) {
            $this->_changedOrderIds[ $orderId ] = true;
        }
    }

    public function setProcessed( $orderId ) {
        $this->_changedOrderIds[ $orderId ] = false;
    }

    public function requestDone() {
        if ( empty( $this->_changedOrderIds ) ) {
            return;
        }
        $this->_db->beginTransaction();
        try {
            foreach ( $this->_changedOrderIds as $orderId => $process ) {
                if ( $process ) {
                    $this->processOrder( $orderId );
                }
            }
            $this->_db->commit();
        } catch ( Exception $e ) {
            $this->_db->rollback();
            throw $e;
        }
    }

    /**
     * Main function that processes the orders, can be called externally or called at request end by requestDone
     *
     * @param <type> $orderId
     */
    public function processOrder( $orderId ) {

        $orderDb = new CartWS_Model_SalesOrderHdr();
        $currentOrders = $orderDb->find( $orderId );
        if ( count($currentOrders) != 1 ) {
            throw new Exception( "Invalid order Id : " . $orderId );
        }
        $this->_currentOrder = $currentOrders->current();
        $formulaDb = new CartWS_Model_FormulaEngine_Db_Formula( $this->_db );
        $formulas = $formulaDb->findActiveFormulas();

        // Un array indexado por formula->type y formula->code.
        $amounts = array();
        $fs = array();
        $continue = array();
        foreach( $formulas as $formula ) {
            $type = $formula->type;
            $code = $formula->code;
            if ( ! isset( $continue[ $type ] ) ) {
                $continue[ $type ] = array();
            }
            // La continuacion puede depender del tipo.
            if ( ! is_array( $continue[ $type ] ) ) {
                continue;
            }

            if ( ! isset( $continue[ $type ][ $code ] ) ) {
                $continue[ $type ][ $code ] = true;
            }
            if ( $continue[ $type ][ $code ] === false ) {
                continue;
            }


            if ( ! isset( $amounts[ $type ] ) ) {
                $amounts[ $type ] = array();
                $fs[ $type ] = array();
            }
            if ( ! isset( $amounts[ $type ][ $code ] ) ) {
                $amounts[ $type ][ $code ] = 0;
                $fs[ $type ][ $code ] = $formula;
            }

            // calculate decide si seguir o no
            $ret = $this->execute( $formula, $amounts );
            switch ( $ret ) {
                case self::RET_CONTINUE:
                    break;
                case self::RET_STOP:
                    $continue[ $type ][ $code ] = false;
                    break;
                case self::RET_TYPE_STOP:
                    $continue[ $type ] = false;
                    break;
                default:
                    throw new Exception( "Invalid formula return value");
                    break;
            }
        }
        $types = CartWS_Model_FormulaEngine_Db_Formula::$types;
        $orderDb = new CartWS_Model_Order();
        $orderDb->deleteLinesByType( $orderId, $types );
        if ( !empty( $amounts ) ) {
            foreach( $amounts as $type => $v ) {
                if ( ! empty( $v ) ) {
                    $lines = array();
                    foreach( $v as $code => $amount ) {
                        $lines[] = array(
                            'sales_order_id' => $orderId,
                            'line_type' => $type,
                            'product_id' => 0,
                            'product_type' => $code,
                            'product_description' => $fs[ $type ][ $code ]->description,
                            'product_detail' => "",
                            'ordered_quantity' => 1,
                            'product_detail' => "",
                            'unit_selling_price' => $amount,
                            'unit_shipping_price' => 0,
                            'line_order' => $fs[ $type ][ $code ]->line_order,
                            'pcnt_tax' => $fs[ $type ][ $code ]->pcnt_tax,
                        );
                    }
                    $orderDb->addOrderLines( $orderId, $lines );
                }
            }
        }

        // If called before request end then don't process at request end.
        $this->setProcessed( $orderId );
    }


    public function execute( $formula, &$amounts ) {
        $class = $formula->class;
        if ( empty( $class ) ) {
            throw new Exception( "La clase para la formula " . $this->formula_id . " no esta configurada" );
        }
        Zend_Loader_Autoloader::autoload( $class );
        $object = new $class( $this, $formula, $amounts );
        if ( ! $object instanceof CartWS_Model_FormulaEngine_Formula_Abstract ) {
            throw new Exception( "La clase $class no es subclase de CartWS_Model_FormulaEngine_Formula_Abstract" );
        }
        return $object->execute();
    }


    protected $_currentOrder = null;
    public function getCurrentOrder() {
        return $this->_currentOrder;
    }

    protected $_currentOrderProperties = null;
    public function getCurrentOrderProperties() {
        if ( $this->_currentOrderProperties === null ) {
            $this->_currentOrder->findCartWS_Model_SalesOrderHdrProperty();
        }
        return $this->_currentOrderProperties;
    }

    protected $_currentOrderLines = null;
    public function getCurrentOrderLines() {
        if ( $this->_currentOrderLines === null ) {
            $this->_currentOrderLines = $this->_currentOrder->findCartWS_Model_SalesOrderLine();
        }
        return $this->_currentOrderLines;
    }

    public function getOrderLineProperties( $line ) {
        $line->findCartWS_Model_SalesLineProperty();
    }

    public function getDbTable( $class ) {
        return Ext_Generic_Model::get( $class, $this->_db );
    }

    public function getDbAdapter() {
        return $this->_db;
    }


}

